﻿#include "QuestInfo.h"
#include "../Settings.h"
#include "../../Shared/Globals.h"

using namespace Server::MirObjects;
using namespace Server::MirEnvir;

namespace Server::MirDatabase
{

    Server::MirEnvir::Envir *QuestInfo::getEnvir()
    {
        return Envir::getMain();
    }

    Server::MirEnvir::Envir *QuestInfo::getEditEnvir()
    {
        return getEnvir()->Edit;
    }

    Server::MessageQueue *QuestInfo::getMessageQueue()
    {
        return MessageQueue::getInstance();
    }

    unsigned int QuestInfo::getFinishNpcIndex() const
    {
        return _finishNpcIndex == 0 ? NpcIndex : _finishNpcIndex;
    }

    void QuestInfo::setFinishNpcIndex(unsigned int value)
    {
        _finishNpcIndex = value;
    }

    NPCObject *QuestInfo::getFinishNPC() const
    {
        return getEnvir()->NPCs.Single([&] (std::any x)
        {
            return x->ObjectID == getFinishNpcIndex();
        });
    }

    QuestInfo::QuestInfo()
    {
    }

    QuestInfo::QuestInfo(BinaryReader *reader)
    {
        Index = reader->ReadInt32();
        Name = reader->ReadString();
        Group = reader->ReadString();
        FileName = reader->ReadString();
        RequiredMinLevel = reader->ReadInt32();

        RequiredMaxLevel = reader->ReadInt32();
        if (RequiredMaxLevel == 0)
        {
            RequiredMaxLevel = std::numeric_limits<unsigned short>::max();
        }

        RequiredQuest = reader->ReadInt32();
        RequiredClass = static_cast<RequiredClass>(reader->ReadByte());
        Type = static_cast<QuestType>(reader->ReadByte());
        GotoMessage = reader->ReadString();
        KillMessage = reader->ReadString();
        ItemMessage = reader->ReadString();
        FlagMessage = reader->ReadString();

        LoadInfo();
    }

    void QuestInfo::Save(BinaryWriter *writer)
    {
        writer->Write(Index);
        writer->Write(Name);
        writer->Write(Group);
        writer->Write(FileName);
        writer->Write(RequiredMinLevel);
        writer->Write(RequiredMaxLevel);
        writer->Write(RequiredQuest);
        writer->Write(static_cast<unsigned char>(RequiredClass));
        writer->Write(static_cast<unsigned char>(Type));
        writer->Write(GotoMessage);
        writer->Write(KillMessage);
        writer->Write(ItemMessage);
        writer->Write(FlagMessage);
    }

    void QuestInfo::LoadInfo(bool clear)
    {
        if (clear)
        {
            ClearInfo();
        }

        if (!FileSystem::directoryExists(Settings::QuestPath))
        {
            return;
        }

        std::string fileName = FileSystem::combine(Settings::QuestPath, FileName + ".txt");

        if (FileSystem::fileExists(fileName))
        {
            std::vector<std::string> lines = File::ReadAllLines(fileName).ToList();

            ParseFile(lines);
        }
        else
        {
            getMessageQueue()->Enqueue(StringHelper::formatSimple("File Not Found: {0}, Quest: {1}", fileName, Name));
        }
    }

    void QuestInfo::ClearInfo()
    {
        Description.clear();
        KillTasks = std::vector<QuestKillTask*>();
        ItemTasks = std::vector<QuestItemTask*>();
        FlagTasks = std::vector<QuestFlagTask*>();
        FixedRewards = std::vector<QuestItemReward*>();
        SelectRewards = std::vector<QuestItemReward*>();
        ExpReward = 0;
        GoldReward = 0;
        CreditReward = 0;
    }

    void QuestInfo::ParseFile(std::vector<std::string> &lines)
    {
        const std::string descriptionCollectKey = "[@DESCRIPTION]", descriptionTaskKey = "[@TASKDESCRIPTION]", descriptionCompletionKey = "[@COMPLETION]", carryItemsKey = "[@CARRYITEMS]", killTasksKey = "[@KILLTASKS]", itemTasksKey = "[@ITEMTASKS]", flagTasksKey = "[@FLAGTASKS]", fixedRewardsKey = "[@FIXEDREWARDS]", selectRewardsKey = "[@SELECTREWARDS]", expRewardKey = "[@EXPREWARD]", goldRewardKey = "[@GOLDREWARD]", creditRewardKey = "[@CREDITREWARD]";

        std::vector<std::string> headers = {descriptionCollectKey, descriptionTaskKey, descriptionCompletionKey, carryItemsKey, killTasksKey, itemTasksKey, flagTasksKey, fixedRewardsKey, selectRewardsKey, expRewardKey, goldRewardKey, creditRewardKey};

        int currentHeader = 0;

        while (currentHeader < headers.size())
        {
            for (int i = 0; i < lines.size(); i++)
            {
                std::string line = StringHelper::toUpper(lines[i]);

                if (line != StringHelper::toUpper(headers[currentHeader]))
                {
                    continue;
                }

                for (int j = i + 1; j < lines.size(); j++)
                {
                    std::string innerLine = lines[j];

                    if (StringHelper::startsWith(innerLine, "["))
                    {
                        break;
                    }
                    if ((lines[j]).empty())
                    {
                        continue;
                    }

//C# TO C++ CONVERTER NOTE: The following 'switch' operated on a string and was converted to C++ 'if-else' logic:
//                    switch (line)
//ORIGINAL LINE: case descriptionCollectKey:
                    if (line == descriptionCollectKey)
                    {
                            Description.push_back(innerLine);
                    }
//ORIGINAL LINE: case descriptionTaskKey:
                    else if (line == descriptionTaskKey)
                    {
                            TaskDescription.push_back(innerLine);
                    }
//ORIGINAL LINE: case descriptionCompletionKey:
                    else if (line == descriptionCompletionKey)
                    {
                            CompletionDescription.push_back(innerLine);
                    }
//ORIGINAL LINE: case carryItemsKey:
                    else if (line == carryItemsKey)
                    {
                            QuestItemTask *t = ParseItem(innerLine);
                            if (t != nullptr)
                            {
                                CarryItems.push_back(t);
                            }
                    }
//ORIGINAL LINE: case killTasksKey:
                    else if (line == killTasksKey)
                    {
                            QuestKillTask *t1 = ParseKill(innerLine);
                            if (t1 != nullptr)
                            {
                                KillTasks.push_back(t1);
                            }
                    }
//ORIGINAL LINE: case itemTasksKey:
                    else if (line == itemTasksKey)
                    {
                            QuestItemTask *t2 = ParseItem(innerLine);
                            if (t2 != nullptr)
                            {
                                ItemTasks.push_back(t2);
                            }
                    }
//ORIGINAL LINE: case flagTasksKey:
                    else if (line == flagTasksKey)
                    {
                            QuestFlagTask *t3 = ParseFlag(innerLine);
                            if (t3 != nullptr)
                            {
                                FlagTasks.push_back(t3);
                            }
                    }
//ORIGINAL LINE: case fixedRewardsKey:
                    else if (line == fixedRewardsKey)
                    {
                                ParseReward(FixedRewards, innerLine);
                    }
//ORIGINAL LINE: case selectRewardsKey:
                    else if (line == selectRewardsKey)
                    {
                                ParseReward(SelectRewards, innerLine);
                    }
//ORIGINAL LINE: case expRewardKey:
                    else if (line == expRewardKey)
                    {
                            unsigned int::TryParse(innerLine, ExpReward);
                    }
//ORIGINAL LINE: case goldRewardKey:
                    else if (line == goldRewardKey)
                    {
                            unsigned int::TryParse(innerLine, GoldReward);
                    }
//ORIGINAL LINE: case creditRewardKey:
                    else if (line == creditRewardKey)
                    {
                            unsigned int::TryParse(innerLine, CreditReward);
                    }
                }
            }

            currentHeader++;
        }
    }

    void QuestInfo::ParseReward(std::vector<QuestItemReward*> &list, const std::string &line)
    {
        if (line.length() < 1)
        {
            return;
        }

        std::vector<std::string> split = StringHelper::split(line, ' ');
        unsigned short count = 1;

        if (split.size() > 1)
        {
            unsigned short::TryParse(split[1], count);
        }

        ItemInfo *mInfo = getEnvir()->GetItemInfo(split[0]);

        if (mInfo == nullptr)
        {
            mInfo = getEnvir()->GetItemInfo(split[0] + "(M)");
            if (mInfo != nullptr)
            {
                QuestItemReward *tempVar = new QuestItemReward();
                tempVar->Item = mInfo;
                tempVar->Count = count;
                list.push_back(tempVar);

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was passed to a method or constructor. Handle memory management manually.
            }

            mInfo = getEnvir()->GetItemInfo(split[0] + "(F)");
            if (mInfo != nullptr)
            {
                QuestItemReward *tempVar2 = new QuestItemReward();
                tempVar2->Item = mInfo;
                tempVar2->Count = count;
                list.push_back(tempVar2);

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar2' statement was not added since tempVar2 was passed to a method or constructor. Handle memory management manually.
            }
        }
        else
        {
            QuestItemReward *tempVar3 = new QuestItemReward();
            tempVar3->Item = mInfo;
            tempVar3->Count = count;
            list.push_back(tempVar3);

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar3' statement was not added since tempVar3 was passed to a method or constructor. Handle memory management manually.
        }
    }

    QuestKillTask *QuestInfo::ParseKill(const std::string &line)
    {
        if (line.length() < 1)
        {
            return nullptr;
        }

        std::vector<std::string> split = StringHelper::split(line, ' ');
        int count = 1;
        std::string message = "";

        MonsterInfo *mInfo = getEnvir()->GetMonsterInfo(split[0]);
        if (split.size() > 1)
        {
            int::TryParse(split[1], count);
        }

        auto match = _regexMessage->Match(line);
        if (match->Success)
        {
            message = match->Groups[1]->Captures[0]->Value;
        }

        QuestKillTask *tempVar = new QuestKillTask();
        tempVar->Monster = mInfo;
        tempVar->Count = count;
        tempVar->Message = message;

        delete tempVar;
        return mInfo == nullptr ? nullptr : tempVar;
    }

    QuestItemTask *QuestInfo::ParseItem(const std::string &line)
    {
        if (line.length() < 1)
        {
            return nullptr;
        }

        std::vector<std::string> split = StringHelper::split(line, ' ');
        unsigned short count = 1;
        std::string message = "";

        ItemInfo *mInfo = getEnvir()->GetItemInfo(split[0]);
        if (split.size() > 1)
        {
            unsigned short::TryParse(split[1], count);
        }

        auto match = _regexMessage->Match(line);
        if (match->Success)
        {
            message = match->Groups[1]->Captures[0]->Value;
        }
        //if (mInfo.StackSize <= 1)
        //{
        //    //recursively add item if cant stack???
        //}

        QuestItemTask *tempVar = new QuestItemTask();
        tempVar->Item = mInfo;
        tempVar->Count = count;
        tempVar->Message = message;

        delete tempVar;
        return mInfo == nullptr ? nullptr : tempVar;
    }

    QuestFlagTask *QuestInfo::ParseFlag(const std::string &line)
    {
        if (line.length() < 1)
        {
            return nullptr;
        }

        std::vector<std::string> split = StringHelper::split(line, ' ');

        int number = -1;
        std::string message = "";

        int::TryParse(split[0], number);

        if (number < 0 || number > Globals::FlagIndexCount - 1000)
        {
            return nullptr;
        }

        auto match = _regexMessage->Match(line);
        if (match->Success)
        {
            message = match->Groups[1]->Captures[0]->Value;
        }

        QuestFlagTask *tempVar = new QuestFlagTask();
        tempVar->Number = number;
        tempVar->Message = message;

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was used in a 'return' or 'throw' statement.
        return tempVar;
    }

    bool QuestInfo::CanAccept(PlayerObject *player)
    {
        if (RequiredMinLevel > player->getLevel() || RequiredMaxLevel < player->getLevel())
        {
            return false;
        }

        if (RequiredQuest > 0 && !std::find(player->CompletedQuests.begin(), player->CompletedQuests.end(), RequiredQuest) != player->CompletedQuests.end())
        {
            return false;
        }

        switch (player->getClass())
        {
            case MirClass::Warrior:
                if (!RequiredClass::HasFlag(RequiredClass::Warrior))
                {
                    return false;
                }
                break;
            case MirClass::Wizard:
                if (!RequiredClass::HasFlag(RequiredClass::Wizard))
                {
                    return false;
                }
                break;
            case MirClass::Taoist:
                if (!RequiredClass::HasFlag(RequiredClass::Taoist))
                {
                    return false;
                }
                break;
            case MirClass::Assassin:
                if (!RequiredClass::HasFlag(RequiredClass::Assassin))
                {
                    return false;
                }
                break;
            case MirClass::Archer:
                if (!RequiredClass::HasFlag(RequiredClass::Archer))
                {
                    return false;
                }
                break;
        }

        return true;
    }

    ClientQuestInfo *QuestInfo::CreateClientQuestInfo()
    {
        ClientQuestInfo *tempVar = new ClientQuestInfo();
        tempVar->Index = Index;
        tempVar->NPCIndex = NpcIndex;
        tempVar->FinishNPCIndex = getFinishNpcIndex();
        tempVar->Name = Name;
        tempVar->Group = Group;
        tempVar->Description = Description;
        tempVar->TaskDescription = TaskDescription;
        tempVar->CompletionDescription = CompletionDescription;
        tempVar->MinLevelNeeded = RequiredMinLevel;
        tempVar->MaxLevelNeeded = RequiredMaxLevel;
        tempVar->ClassNeeded = RequiredClass;
        tempVar->QuestNeeded = RequiredQuest;
        tempVar->Type = Type;
        tempVar->RewardGold = GoldReward;
        tempVar->RewardExp = ExpReward;
        tempVar->RewardCredit = CreditReward;
        tempVar->RewardsFixedItem = FixedRewards;
        tempVar->RewardsSelectItem = SelectRewards;

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was used in a 'return' or 'throw' statement.
        return tempVar;
    }

    void QuestInfo::FromText(const std::string &text)
    {
        std::vector<std::string> data = text.Split(std::vector<char> {','});

        if (data.size() < 10)
        {
            return;
        }

        QuestInfo *info = new QuestInfo();

        info->Name = data[0];
        info->Group = data[1];

        unsigned char temp;

        unsigned char::TryParse(data[2], temp);

        info->Type = static_cast<QuestType>(temp);

        info->FileName = data[3];
        info->GotoMessage = data[4];
        info->KillMessage = data[5];
        info->ItemMessage = data[6];
        info->FlagMessage = data[7];

        int::TryParse(data[8], info->RequiredMinLevel);
        int::TryParse(data[9], info->RequiredMaxLevel);
        int::TryParse(data[10], info->RequiredQuest);

        unsigned char::TryParse(data[11], temp);

        info->RequiredClass = static_cast<RequiredClass>(temp);

        setEditEnvir(getEditEnvir() + 1);
        info->Index = getEditEnvir()->QuestIndex;
        getEditEnvir()->QuestInfoList.push_back(info);

//C# TO C++ CONVERTER TODO TASK: A 'delete info' statement was not added since info was passed to a method or constructor. Handle memory management manually.
    }

    std::string QuestInfo::ToText()
    {
        return std::string::Format("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11}", Name, Group, static_cast<unsigned char>(Type), FileName, GotoMessage, KillMessage, ItemMessage, FlagMessage, RequiredMinLevel, RequiredMaxLevel, RequiredQuest, static_cast<unsigned char>(RequiredClass));
    }

    std::string QuestInfo::ToString()
    {
        return StringHelper::formatSimple("{0}:   {1}", Index, Name);
    }
}
